#[[
 * Copyright (c) 2024 iSOFT INFRASTRUCTURE SOFTWARE CO., LTD.
 * easyAda is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
]]

cmake_minimum_required(VERSION 3.7.2)

macro(generate_define define_list name value comment)
    set(define_str "#define CONFIG_${name}  ${value}")
    if(NOT "${comment}" STREQUAL "")
        # include in liner files can't use double slash comments
        string(APPEND define_str "  /* ${comment} */")
    endif()
    list(APPEND ${define_list} ${define_str} )
endmacro(generate_define)


function(check_valid deps valid)
    set(res ON)
    if(NOT "${deps}" STREQUAL "")
        foreach(test ${deps})
            string(REGEX REPLACE " +" ";" test "${test}" )
            if(NOT (${test}))
                set(res OFF)
                break()
            endif()
        endforeach()
    endif()
    set(${valid} ${res} PARENT_SCOPE)
endfunction(check_valid)


function(config_option optionname configname doc)
    cmake_parse_arguments(PARSE_ARGV 3 "CONFIG" "" "DEPENDS;DEFAULT" "")
    if(NOT "${CONFIG_UNPARSED_ARGUMENTS}" STREQUAL "")
        message(FATAL_ERROR "Unknown arguments to config_option")
    endif()
    check_valid("${CONFIG_DEPENDS}" valid)
    set(local_config_string "${configure_string}")
    if(valid)
        if(DEFINED ${optionname} AND (NOT DEFINED ${optionname}_DISABLED)) 
            set(${optionname} "${${optionname}}" CACHE BOOL "${doc}" FORCE)
        else()
            set(${optionname} "${CONFIG_DEFAULT}" CACHE BOOL "${doc}" FORCE)
             unset(${optionname}_DISABLED CACHE)
        endif()
    else()
        unset(${optionname} CACHE)
        set(${optionname}_DISABLED TRUE CACHE INTERNAL "" FORCE)
    endif()

    if(${optionname})
        generate_define(local_config_string ${configname} 1 "${optionname}=${${optionname}}")
    endif()
    set(configure_string "${local_config_string}" PARENT_SCOPE)
endfunction(config_option)


macro(config_set optionname configname value)
    set(${optionname} "${value}" CACHE INTERNAL "" FORCE)
    if("${value}" STREQUAL "ON")
        generate_define(configure_string "${configname}" "1" "")
    else()
        generate_define(configure_string "${configname}" "${value}" "")
    endif()
endmacro(config_set)


function(config_string optionname configname doc)
    cmake_parse_arguments(PARSE_ARGV 3 
        "CONFIG" "UNQUOTE;" "DEPENDS;DEFAULT" "")
    if(NOT "${CONFIG_UNPARSED_ARGUMENTS}" STREQUAL "")
        message(FATAL_ERROR "Unknown arguments to config_option: ${CONFIG_UNPARSED_ARGUMENTS}")
    endif()
    if("${CONFIG_DEFAULT}" STREQUAL "")
        message(FATAL_ERROR "No default specified for config_option")
    endif()

    set(local_config_string "${configure_string}")

    check_valid("${CONFIG_DEPENDS}" valid)
    if(valid)
        set(force "")
        if(${optionname}_UNAVAILABLE)
            set(force "FORCE")
            unset(${optionname}_UNAVAILABLE CACHE)
        endif()
        set(${optionname} "${CONFIG_DEFAULT}" CACHE STRING "${doc}" ${force})
    else()
        set(${optionname}_UNAVAILABLE ON CACHE INTERNAL "" FORCE)
        unset(${optionname} CACHE)
    endif()
    if(${optionname})
        if(CONFIG_UNQUOTE)
            set(quote "")
        else()
            set(quote "\"")
        endif()
        generate_define(local_config_string ${configname} "${quote}@${optionname}@${quote}" "")
    endif()
    set(configure_string "${local_config_string}" PARENT_SCOPE)
endfunction(config_string)


function(config_choice optionname configname doc)
    set(local_config_string "${configure_string}")
    math(EXPR limit "${ARGC} - 1")
    set(force_default "")
    set(first ON)
    set(found_current OFF)
    foreach(i RANGE 3 ${limit})
        set(option "${ARGV${i}}")
        list(GET option 0 option_value)
        list(GET option 1 option_cache)
        list(GET option 2 option_config)
        list(REMOVE_AT option 0 1 2)

        set(valid ON)
        foreach(truth IN LISTS option)
            string(REGEX REPLACE " +" ";" truth "${truth}")
            if(NOT (${truth}))
                if("${${optionname}}" STREQUAL "${option_value}")
                    set(force_default "FORCE")
                endif()
                set(valid OFF)
            endif()
        endforeach()

        if(valid)
            if(first)
                set(default "${option_value}")
                set(first_cache "${option_cache}")
                set(first_config "${option_config}")
                set(first OFF)
            endif()
            list(APPEND strings "${option_value}")
            if("${${optionname}}" STREQUAL "${option_value}")
                set(${option_cache} ON CACHE INTERNAL "" FORCE)
                generate_define(local_config_string "${option_config}" "1" "")
                set(found_current ON)
            else()
                set(${option_cache} OFF CACHE INTERNAL "" FORCE)
            endif()
        else()
            unset(${option_cache} CACHE)
        endif()
    endforeach()
    if(NOT found_current)
        set(force_default "FORCE")
    endif()
    if(first)
        unset(${optionname} CACHE)
    else()

        set(${optionname} "${default}" CACHE STRING "${doc}" ${force_default})

        set_property(CACHE ${optionname} PROPERTY STRINGS ${strings})
        if(NOT found_current)
            set(${first_cache} ON CACHE INTERNAL "" FORCE)
            generate_define(local_config_string "${first_config}" "1" "")
        endif()
    endif()

    set(configure_string "${local_config_string}" PARENT_SCOPE)

endfunction(config_choice)


function(write_autoconfig autofile configure_template)
    string(CONFIGURE "${configure_template}" config_header_contents)
    string(REPLACE ";" "\n" config_header_contents "${config_header_contents}" )
    file(GENERATE OUTPUT "${autofile}" CONTENT "\n#pragma once\n\n${config_header_contents}")
endfunction(write_autoconfig)


macro(declare_platform name config1 config2 enable_test)
    list(APPEND kernel_platforms "${name}\;${config1}\;${config2}\;${enable_test}")
    if("${KernelPlatform}" STREQUAL ${name})
        set(${config1} ON CACHE INTERNAL "" FORCE)
        set(KernelPlatform ${KernelPlatform} CACHE STRING "")
        set(KernelPlatformFind ON)
    else()
        set(${config1} OFF CACHE INTERNAL "" FORCE)
    endif()
endmacro()


function(add_sources)
    cmake_parse_arguments(PARSE_ARGV 0 "ADD" "" "DEP;PREFIX" "CFILES;ASMFILES")
    if(NOT "${ADD_UNPARSED_ARGUMENTS}" STREQUAL "")
        message(FATAL_ERROR "Unknown arguments to add_c_sources: ${ADD_UNPARSED_ARGUMENTS}")
    endif()

    if(NOT "${ADD_PREFIX}" STREQUAL "")
        set(ADD_PREFIX "${ADD_PREFIX}/")
    endif()

    set(local_c_sources ${c_sources})
    set(local_asm_sources ${asm_sources})
    set(ADD_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/${ADD_PREFIX}")

    foreach(file IN LISTS ADD_CFILES)
        list(APPEND local_c_sources "${ADD_PREFIX}${file}")
    endforeach()

    foreach(file IN LISTS ADD_ASMFILES)
        list(APPEND local_asm_sources "${ADD_PREFIX}${file}")
    endforeach()

    set(valid ON)
    foreach(truth IN ITEMS ${ADD_DEP})
        string(REGEX REPLACE " +" ";" truth "${truth}")
        if(NOT (${truth}))
            set(valid OFF)
            break()
        endif()
    endforeach()

    if(valid)
        set(c_sources ${local_c_sources} PARENT_SCOPE)
        set(asm_sources ${local_asm_sources} PARENT_SCOPE)
    endif()

endfunction(add_sources)


function(generate_toolchainfile)
    set(toolchain_file "gcc.cmake")
    if(
            ("${CMAKE_TOOLCHAIN_FILE}" STREQUAL "")
            OR ("${CMAKE_TOOLCHAIN_FILE}" STREQUAL "${toolchain_file}.temp")
        )
        if(DEFINED CACHE{CROSS_COMPILER_PREFIX})
            set(cross_prefix $CACHE{CROSS_COMPILER_PREFIX})
        endif()

        if(NOT cross_prefix AND NOT "$ENV{CROSS_COMPILE}" STREQUAL "")
            message("CROSS_COMPILE=$ENV{CROSS_COMPILE}")
            set(cross_prefix "$ENV{CROSS_COMPILE}")
        elseif(cross_prefix)
            message("cross_prefix=${cross_prefix}")
        endif()

        configure_file(
            "${CMAKE_CURRENT_LIST_DIR}/cmake/${toolchain_file}" "${toolchain_file}.temp" @ONLY
        )

        set(CMAKE_TOOLCHAIN_FILE "${toolchain_file}.temp" CACHE PATH "")
    endif()

    if (NOT CMAKE_BUILD_TYPE)
        set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build." FORCE)
        set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release"
            "MinSizeRel" "RelWithDebInfo")
    endif()
endfunction(generate_toolchainfile)
